package triannon.client.util;

import java.util.Iterator;
import java.util.NoSuchElementException;

public class ThreeDimensionalArrayIterator<T> implements Iterator<T> {
	public enum Order {
		XYZ, ZXY, ZYX, XZY
	}

	private final T[][][] field;
	private Order order = Order.XYZ;

	private int x;
	private int y;
	private int z;

	private final int offsetX;
	private final int offsetY;
	private final int offsetZ;

	private final int lengthX;
	private final int lengthY;
	private final int lengthZ;

	private boolean hasNextItem;
	private T nextItem;

	public ThreeDimensionalArrayIterator(T[][][] field) {
		this.field = field;

		this.offsetX = 0;
		this.offsetY = 0;
		this.offsetZ = 0;

		this.lengthX = field.length;
		this.lengthY = field[0].length;
		this.lengthZ = field[0][0].length;

		// don't forget to reset on initialization
		reset();
	}

	public ThreeDimensionalArrayIterator(T[][][] field, Order order) {
		this.field = field;

		this.offsetX = 0;
		this.offsetY = 0;
		this.offsetZ = 0;

		this.lengthX = field.length;
		this.lengthY = field[0].length;
		this.lengthZ = field[0][0].length;

		this.order = order;

		// don't forget to reset on initialization
		reset();
	}

	public ThreeDimensionalArrayIterator(T[][][] field, int offsetX, int offsetY,
			int offsetZ, int lengthX, int lengthY, int lengthZ) {
		this.field = field;

		this.offsetX = offsetX;
		this.offsetY = offsetY;
		this.offsetZ = offsetZ;

		this.lengthX = Math.min(lengthX + offsetX, field.length);
		this.lengthY = Math.min(lengthY + offsetY, field[0].length);
		this.lengthZ = Math.min(lengthZ + offsetZ, field[0][0].length);

		// don't forget to reset on initialization
		reset();

		// System.err.println("from x: " + offsetX + " from y: " + offsetY);
		// System.err.println("x: " + x + " y: " + y + " z: " + z);
	}

	public void reset() {
		x = offsetX;
		y = offsetY;
		z = offsetZ;
		hasNextItem = false;
		nextItem = null;

		calcNext();
	}

	private void calcNext() {
		hasNextItem = false;

		if (order == Order.ZYX) {
			for (; x < lengthX; x++) {
				for (; y < lengthY; y++) {
					for (; z < lengthZ; z++) {
						if (field[x][y][z] != null) {
							nextItem = field[x][y][z];
							hasNextItem = true;
							z++;
							return;
						}
					}
					z = offsetZ;
				}
				y = offsetY;
			}

		} else if (order == Order.ZXY) {
			for (; y < lengthY; y++) {
				for (; x < lengthX; x++) {
					for (; z < lengthZ; z++) {
						if (field[x][y][z] != null) {
							nextItem = field[x][y][z];
							hasNextItem = true;
							z++;
							return;
						}
					}
					z = offsetZ;
				}
				x = offsetX;
			}

		} else if (order == Order.XYZ) {
			for (; z < lengthZ; z++) {
				for (; y < lengthY; y++) {
					for (; x < lengthX; x++) {
						if (field[x][y][z] != null) {
							// System.err.println(x + "," + y + "," + z);
							nextItem = field[x][y][z];
							hasNextItem = true;
							x++;
							return;
						}
					}
					x = offsetX;
				}
				y = offsetY;
			}
		} else if (order == Order.XZY) {
			for (; y < lengthY; y++) {
				for (; z < lengthZ; z++) {
					for (; x < lengthX; x++) {
						if (field[x][y][z] != null) {
							// System.err.println(x + "," + y + "," + z);
							nextItem = field[x][y][z];
							hasNextItem = true;
							x++;
							return;
						}
					}
					x = offsetX;
				}
				z = offsetZ;
			}
		}

	}

	@Override
	public boolean hasNext() {
		return hasNextItem;
	}

	@Override
	public T next() {
		if (!hasNext()) {
			throw new NoSuchElementException();
		}

		T next = nextItem;

		calcNext();

		return next;
	}

	@Override
	public void remove() {
		field[x][y][z] = null;
		calcNext();
	}
}
